// 是不是仅读  仅读的属性set时会报异常

import { track, trigger } from "./effect.js"

/**
 * 
 * @param {*} isReadonly 
 * @param {*} shallow 
 * @returns 
 */
function createGetter(isReadonly = false, shallow = false) {
    return function get(target, key, receiver) {

        // 源码中对数组进行了判断
        // 重写includes indexOf lastIndexOf,要查询的值一变就要收集依赖
        // 重写push pop shift unshift splice 都会改变原数组，都会访问length属性 防止length被修改，无限更新的问题，停止依赖收集
        // effect(() => {
        //     arr.push(1) 
        // })
        // effect(() => {
        //     arr.push(2) 
        // })

        const res = Reflect.get(target, key, receiver) // target[key]
        if (!isReadonly) {
            // 不是只读，则收集依赖
            track(target, 'get', key)
            console.log('执行effect会取值，收集effect')
        }
        if (shallow) {
            // 浅的，则直接返回，proxy默认会代理第一层
            return res
        }
        if (res instanceof Object) {
            // 如果对象中还有对象，则需要根据情况来代理
            // vue2一开始就递归
            // vue3取值才去代理，懒代理
            return isReadonly ? readonly(res) : reactive(res)
        }
        return res
    }
}

function createSetter(shallow = false) {
    return function set(target, key, value, receiver) {
        // console.log(typeof key)
        const oldValue = target[key]

        // 区分是新增还是修改
        // 是数组 而且修改的是索引(索引是数字类型) Array.isArray(target) && parseInt(key) + ''===key
        // 修改的索引是不是数组长度以内 Number(key) < target.length  不是长度以内就是新增属性
        // 对象的话，就看本身有没有这个属性，有修改，没有则新增
        let hadKey = Array.isArray(target) && parseInt(key) + ''===key ? Number(key) < target.length : Object.hasOwnProperty.call(target, key)
        const res = Reflect.set(target, key, value, receiver)
        console.log(hadKey)
        if (!hadKey) {//新增
            trigger(target, 'add', key, value)
        } else if (oldValue !== value) {//老值和新值是否一致
            trigger(target, 'set', key, value, oldValue)
        }
       
        return res
    }
}

const get = createGetter()
const shallowGet = createGetter(false, true)
const readonlyGet = createGetter(true)
const shallowReactiveGet = createGetter(true, true)

const set = createSetter()
const shallowSet = createSetter(true)

const reactiveHandlers = {
    get,
    set
}
const shallowReactiveHandlers = {
    get: shallowGet,
    set: shallowSet
}
const readonlyHandlers = {
    get: readonlyGet,
    set: (target, key, value) => {
        console.warn('set on key ' + key)
    }
}
const shallowReadonlyHandlers = {
    get: shallowReactiveGet,
    set: (target, key, value) => {
        console.warn('set on key ' + key)
    }
}

export function reactive(target) {return (createReactiveObject(target, false, reactiveHandlers))}
export function shallowReactive(target) {return (createReactiveObject(target, false, shallowReactiveHandlers))}
export function readonly(target) {return (createReactiveObject(target, true, readonlyHandlers))}
export function shallowReadonly(target) {return (createReactiveObject(target, true, shallowReadonlyHandlers))}

/**
 * 
 * @param {*} target 
 * @param {*} isReadonly 
 * @param {*} baseHandlers 
 * @returns proxy
 * 
 * 是不是仅读 是不是深度 
 * 函数柯里化
 * new Proxy()
 * 数据的读取和修改 get set
 */
const reactiveMap = new WeakMap()
const readonlyMap = new WeakMap()
function createReactiveObject(target, isReadonly, baseHandlers) {
    // 如果目标不是对象，没法拦截了，reactive只能拦截对象类型
    if (!target instanceof Object) {
        return target
    } 
    // 如果目标对象已经代理过了 就不能再次代理了 
    // 可能一个对象被readonly代理，又被shallowReadonly代理
    const proxyMap = isReadonly ? readonlyMap : reactiveMap
    const existPeoxy = proxyMap.get(target)
    // 已经代理了，直接返回
    if (existPeoxy) {
        return existPeoxy
    }
    const proxy =  new Proxy(target, baseHandlers)
    proxyMap.set(target, proxy)
    return proxy
}